| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- import { _decorator, Component, Layers, math, Node, Quat, Vec2, Vec3 } from 'cc';
- const { ccclass, property } = _decorator;
- @ccclass('UsefulTools')
- export class UsefulTools {
- //是否getter方法
- static isGetter(obj: any, prop: string): boolean {
- const descriptor =
- Object.getOwnPropertyDescriptor(obj, prop) || Object.getOwnPropertyDescriptor(Object.getPrototypeOf(obj), prop);
- return !!descriptor?.get;
- }
- //安全地取属性值(变量/getter方法/普通方法)
- static safeGetValue(obj: any, prop: string): any {
- const descriptor =
- Object.getOwnPropertyDescriptor(obj, prop) || Object.getOwnPropertyDescriptor(Object.getPrototypeOf(obj), prop);
- if (descriptor?.get) {
- return obj[prop]; // 触发 getter
- } else if (typeof obj[prop] === 'function') {
- return obj[prop](); // 执行普通方法
- } else {
- return obj[prop]; // 返回普通属性
- }
- }
- /**
- * 判断一个多边形(顶点有序)的顶点数组是否存在自相交
- * @param points Vec2[] 多边形顶点(顺序排列,首尾可不闭合)
- * @returns result 是否自相交 ,index 自相交的第一个点的索引
- * 注意:Vec3的z轴会被忽略
- */
- public static isPolygonSelfIntersect(points: Vec2[] | Vec3[]): { result: boolean; index: number } {
- const n = points.length;
- if (n < 4) return { result: false, index: -1 }; // 三角形不可能自相交
- // 检查所有非相邻边是否相交(仅检查真正非相邻的边)
- for (let i = 0; i < n - 1; i++) {
- const a1 = points[i];
- const a2 = points[i + 1];
- for (let j = i + 2; j < n - 1; j++) {
- // 跳过相邻边
- if (i === 0 && j === n - 2) continue; // 首尾边不算
- const b1 = points[j];
- const b2 = points[j + 1];
- if (this.segmentsIntersect(a1, a2, b1, b2)) {
- return { result: true, index: i };
- }
- }
- }
- return { result: false, index: -1 };
- }
- /**
- * 判断两线段是否相交
- * 注意:Vec3的z轴会被忽略
- */
- public static segmentsIntersect(p1: Vec2 | Vec3, p2: Vec2 | Vec3, q1: Vec2 | Vec3, q2: Vec2 | Vec3): boolean {
- // 快速排斥
- // 快速排斥
- if (
- Math.max(p1.x, p2.x) < Math.min(q1.x, q2.x) ||
- Math.max(q1.x, q2.x) < Math.min(p1.x, p2.x) ||
- Math.max(p1.y, p2.y) < Math.min(q1.y, q2.y) ||
- Math.max(q1.y, q2.y) < Math.min(p1.y, p2.y)
- ) {
- return false;
- }
- const d1 = this.direction(q1, q2, p1);
- const d2 = this.direction(q1, q2, p2);
- const d3 = this.direction(p1, p2, q1);
- const d4 = this.direction(p1, p2, q2);
- // Proper intersection
- if (d1 * d2 < 0 && d3 * d4 < 0) {
- return true;
- }
- // Special Cases: check for colinear and endpoint overlap
- if (d1 === 0 && this.onSegment(q1, q2, p1)) return true;
- if (d2 === 0 && this.onSegment(q1, q2, p2)) return true;
- if (d3 === 0 && this.onSegment(p1, p2, q1)) return true;
- if (d4 === 0 && this.onSegment(p1, p2, q2)) return true;
- return false;
- }
- // 判断点c是否在线段ab上(假设三点共线)
- private static onSegment(a: Vec2 | Vec3, b: Vec2 | Vec3, c: Vec2 | Vec3): boolean {
- return (
- Math.min(a.x, b.x) <= c.x && c.x <= Math.max(a.x, b.x) && Math.min(a.y, b.y) <= c.y && c.y <= Math.max(a.y, b.y)
- );
- }
- // * 计算向量叉积
- // * 注意:Vec3的z轴会被忽略
- // */
- public static direction(a: Vec2 | Vec3, b: Vec2 | Vec3, c: Vec2 | Vec3): number {
- return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
- }
- //从一组Vec2[]或者Vec3[]中判断所有点是否共线
- //注意: Vec3的z轴会被忽略
- //返回值: { result: boolean; index: 共线的第一个点索引值 }
- public static isCollinear(points: Vec2[] | Vec3[]): { result: boolean; index: number } {
- if (points.length < 3) return { result: false, index: -1 };
- for (let i = 0; i < points.length - 2; i++) {
- const a = points[i];
- const b = points[i + 1];
- const c = points[i + 2];
- if (this.isCollinearThreePoints(a, b, c)) {
- return { result: true, index: i };
- }
- }
- return { result: false, index: -1 };
- }
- //判断三点是否共线
- //注意: Vec3的z轴会被忽略
- public static isCollinearThreePoints(a: Vec2 | Vec3, b: Vec2 | Vec3, c: Vec2 | Vec3): boolean {
- const abx = b.x - a.x;
- const aby = b.y - a.y;
- const acx = c.x - a.x;
- const acy = c.y - a.y;
- const cross = abx * acy - aby * acx;
- return Math.abs(cross) < 0.1; // 使用叉积判断共线
- }
- //判断一组Vec2[]或者Vec3[]中是否有重复点
- //注意: Vec3[]的z轴会被忽略
- public static hasDuplicatePoints(points: Vec2[] | Vec3[]): boolean {
- for (let i = 0; i < points.length; i++) {
- for (let j = i + 1; j < points.length; j++) {
- if (points[i].x === points[j].x && points[i].y === points[j].y) {
- return true;
- }
- }
- }
- return false;
- }
- /**
- * 生成一个随机五位字符串
- * @returns {string}
- */
- public static randomTmpIDString(): string {
- const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
- let result = '';
- for (let i = 0; i < 5; i++) {
- result += chars.charAt(Math.floor(Math.random() * chars.length));
- }
- return result;
- }
- // 计算当前颜色的HSL值
- public static rgbToHsl(r: number, g: number, b: number) {
- r /= 255;
- g /= 255;
- b /= 255;
- const max = Math.max(r, g, b),
- min = Math.min(r, g, b);
- let h = 0,
- s = 0,
- l = (max + min) / 2;
- if (max !== min) {
- const d = max - min;
- s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
- switch (max) {
- case r:
- h = (g - b) / d + (g < b ? 6 : 0);
- break;
- case g:
- h = (b - r) / d + 2;
- break;
- case b:
- h = (r - g) / d + 4;
- break;
- }
- h /= 6;
- }
- return [h, s, l];
- }
- // 将HSL转换为RGB
- public static hslToRgb(h: number, s: number, l: number) {
- let r, g, b;
- if (s === 0) {
- r = g = b = l;
- } else {
- const hue2rgb = (p: number, q: number, t: number) => {
- if (t < 0) t += 1;
- if (t > 1) t -= 1;
- if (t < 1 / 6) return p + (q - p) * 6 * t;
- if (t < 1 / 2) return q;
- if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
- return p;
- };
- const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
- const p = 2 * l - q;
- r = hue2rgb(p, q, h + 1 / 3);
- g = hue2rgb(p, q, h);
- b = hue2rgb(p, q, h - 1 / 3);
- }
- return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
- }
- /**
- * 比较两个版本号(格式如 "3.8.0")
- * @param v1 版本号1
- * @param v2 版本号2
- * @returns
- * - 正数:v1 > v2
- * - 负数:v1 < v2
- * - 0:v1 == v2
- */
- public static compareVersions(v1: string, v2: string): number {
- // 1. 分割版本号为数字数组
- const parts1 = v1.split('.').map(Number);
- const parts2 = v2.split('.').map(Number);
- // 2. 逐级比较(从左到右)
- for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
- const num1 = parts1[i] || 0; // 缺失的位补0
- const num2 = parts2[i] || 0;
- if (num1 !== num2) {
- return num1 - num2; // 优先级高的位不等时直接返回结果
- }
- }
- // 3. 所有位均相等
- return 0;
- }
- //检测某层名字是否已设置
- public static layerExist(name: string): boolean {
- const n = Layers.nameToLayer(name);
- if (n > 0) return true;
- const lname = Layers.layerToName(0);
- if (lname !== name) {
- return false;
- }
- return true;
- }
- //检查并添加新层,添加后在编辑器设置中可能未刷新.
- public static checkAndAddLayerByName(name: string): number {
- if (this.layerExist(name)) {
- console.log(`${name} 已存在 ,无需获取空闲位置`);
- return 1 << Layers.nameToLayer(name);
- }
- let n = -1;
- for (let i = 0; i < 20; i++) {
- if (Layers.layerToName(i) == undefined) {
- n = i;
- break;
- }
- }
- if (n !== -1) {
- Layers.addLayer(name, n);
- console.warn(`添加新层: ${name},位置: ${n} ,如果在编辑器中,你需要关闭 项目设置 窗口后重新打开才能正常显示`);
- return 1 << n;
- } else {
- console.warn(`无法添加新层: ${name},没有可用的层位置。请检查层设置。`);
- return -1;
- }
- }
- }
|